import com.sun.deploy.util.StringUtils;
import org.junit.Test;
import pojo.Book;
import pojo.Type;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author gzkemays
 * @date 2020/11/11 14:16
 */
public class jStream {
    // 初始化 book list
    private List<Book> getBookList() {
        List<Book> list = Arrays.asList(
                new Book(1,"Java 必学","学习书籍",16.0),
                new Book(2,"Python 必学","学习书籍",15.0),
                new Book(3,"Mysql 必学","学习书籍",17.0),
                new Book(4,"Java 必学","学习书籍",18.5),
                new Book(5,"前端必学","学习书籍",20.0)
        );
        return list;
    }
    private List<Type> getTypeList() {
        ArrayList<String> list1 = new ArrayList<>(Arrays.asList("生活用品","化妆美颜","时尚潮流"));
        ArrayList<String> list2 = new ArrayList<>(Arrays.asList("维修工具","电子设备"));
        List<Type> list = Arrays.asList(
                new Type(1,list1),
                new Type(2,list2)
                );
        return list;
    }
    @Test
    // 简单案例
    public void streamExample() {
        // 筛选分类为学习书籍的书本求价格总和。
        double sum = getBookList().stream()
                                    .filter(book -> book.getType().equals("学习书籍"))
                                    .mapToDouble(Book::getPrice)
                                    .sum();
        System.out.println("sum = " + sum);
    }

    @Test
    // 流的构造
    public void buildStream() {
        // 直接构造
        Stream stream = Stream.of(1,2,3,4,5);
        // 数组构造
        Integer[] intArray = new Integer[] {1,2,3,4,5};
        // 1、直接说明流来源
        stream = Stream.of(intArray);
        // 2、通过 Arrays 指定来源
        stream = Arrays.stream(intArray);
        // 列表构造
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        stream = list.stream();
    }

    @Test
    // 流转换
    public void streamConverter() {
        // 初始化流
        List<String> list = Arrays.asList("1","2","3","4","1","2","5");
        Stream stream = list.stream();

        // 流转 String 数组
        String[] strArray = (String[]) stream.toArray(String[]::new);

        // 流转 String 集合
        List<String> strList = (List<String>) stream.collect(Collectors.toList());
        List<String> strList2 = (List<String>) stream.collect(Collectors.toCollection(ArrayList::new));
        Set set = (Set) stream.collect(Collectors.toSet());
        Stack stack = (Stack) stream.collect(Collectors.toCollection(Stack::new));

        // String
        String str = stream.collect(Collectors.joining()).toString();

        System.out.println("strArray = " + strArray);
        System.out.println("strList = " + strList);
        System.out.println("strList2 = " + strList2);
        System.out.println("set = " + set);
        System.out.println("stack = " + stack);
        System.out.println("str = " + str);

    }

    @Test
    public void mapStream() {
        // 转大写
        List<String> list = Arrays.asList("aabc","abcd","efg");
        List<String> collect = list.parallelStream()
                                    .map(String::toUpperCase)
                                    .collect(Collectors.toList());
        System.out.println("collect = " + collect);

        // 数据扁平化
        Stream<List<Integer>> inputStream = Stream.of(
                Arrays.asList(1),
                Arrays.asList(2, 3),
                Arrays.asList(4, 5, 6)
        );
        List<Integer> outputStream = inputStream.
                flatMap(Collection::stream).collect(Collectors.toList());
        System.out.println("outputStream = " + outputStream);

        // 对象扁平化
        List<Book> bookList = getBookList();
        List<String> collect1 = bookList.parallelStream()
                .flatMap(book -> Stream.of(book.getName()))
                .filter(b -> b.contains("Java"))
                .collect(Collectors.toList());
        System.out.println("collect1 = " + collect1);
    }
    @Test
    public void peekStream() {
        Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e))
                .collect(Collectors.toList());
    }

    @Test
    public void findFirst() {
        // 查找第一个元素，如果不为空则输出该元素。
        Stream.of("one", "two", "three", "four")
                .findFirst().ifPresent(System.out::println);
//        System.out.println("equals = " + first);
    }

    @Test
    public void reduceStream() {
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        Integer sum = list.stream()
                           .reduce(0,Integer::sum);
        Integer sum2 = list.stream()
                            .reduce(1, (a,b) -> a*b);
        // 15
        System.out.println("sum = " + sum);
        // 120
        System.out.println("sum2 = " + sum2);
        List<Type> types = getTypeList();
        Optional<List<String>> reduce = types.stream()
                                                .filter(Objects::nonNull)
                                                .map(Type::getChildList)
                                                .reduce((a, b) -> {
                                                a.addAll(b);
                                                return a;
                                                });
        reduce.ifPresent(System.out::println);
    }

    @Test
    public void skipAndLimit() {
        List<Book> list = new ArrayList<>();
        for (int i = 0; i<10000; i++) {
            Book book = new Book(i,"name"+i,"type"+i, (double) i);
            list.add(book);
        }
        List<String> collect = list.stream()
                                    .map(Book::getName)
                                    .limit(10)
                                    .skip(3)
                                    .collect(Collectors.toList());
        System.out.println("collect = " + collect);

        List<Book> slist = new ArrayList<>();
        for (int i = 1; i<=5; i++) {
            Book book = new Book(i,"name"+i,"type"+i, (double) i);
            slist.add(book);
        }
        // 受制于 sorted 影响
        List<Book> collect1 = slist.stream()
                                    .sorted(Comparator.comparing(a -> ((Integer) a.getId()))).limit(2)
                                    .collect(Collectors.toList());
        // 不受制于 sorted
        List<Book> collect2 = slist.stream()
                                    .limit(2)
                                    .sorted(Comparator.comparing(a -> ((Integer) a.getId())))
                                    .collect(Collectors.toList());
        System.out.println("collect1 = " + collect1);
        System.out.println("collect1 = " + collect2);
    }

    @Test
    public void minAndMaxAndDistinct() {
        List<String> list = Arrays.asList("efghyj","aabc","klm","dn","opqrstuvw");
        // 获取最大长度的字符串的长度。
        int asInt = list.stream()
                            .mapToInt(String::length)
                            .max()
                            .getAsInt();
        System.out.println("asInt = " + asInt);

        // 排除重复的词 aabc 中的一个 a
        List<String> collect = list.stream()
                                    .flatMap(e -> Stream.of(e.split("")))
                                    .filter(word -> word.length() > 0)
                                    .map(String::toUpperCase)
                                    .distinct()
                                    .sorted()
                                    .collect(Collectors.toList());
        System.out.println("collect = " + collect);
    }

    @Test
    public void matchStream() {
        List<Book> slist = new ArrayList<>();
        for (int i = 1; i<=5; i++) {
            Book book = new Book(i,"name"+i,"type"+i, (double) i);
            slist.add(book);
        }

        // 有不是为 1 返回 false
        boolean b1 = slist.stream().allMatch(b -> b.getPrice() > 1);
        // 有 1 返回 true
        boolean b2 = slist.stream().anyMatch(b -> b.getPrice() > 1);
        // 包含 1 返回 false
        boolean b3 = slist.stream().noneMatch(b -> b.getPrice() > 1);
        System.out.println("b1 = " + b1 + ", b2 = " + b2 +", b3 = " + b3);
    }

    @Test
    public void iterateStream() {
        // k 为 3 的等差数列
        Stream.iterate(0, n -> n + 3)
                .limit(10)
                .forEach(x -> System.out.print(x + " "));
    }

    @Test
    public void streamGenerate() {
        Random random = new Random();
        // Supplier 自生成随机数
        Supplier<Integer> supplier = random::nextInt;
        Stream.generate(supplier)
                .limit(10)
                .forEach(System.out::println);
        // Int 流生成随机数
        IntStream.generate(random::nextInt)
                    .limit(10)
                    .forEach(System.out::println);
        // 自构建 Supplier
        Stream.generate(new BookSupplier())
                .limit(10)
                .forEach(System.out::println);
    }

    // 继承 Supplier.get() 用于创建新的对象。
    private class BookSupplier implements Supplier<Book> {
        private Integer index;
        private Random random = new Random();
        @Override
        public Book get() {
            return new Book(Optional.ofNullable(index).isPresent()?
                    0:Optional.ofNullable(index)
                                .orElseGet(() -> {
                                    return random.nextInt(100);
                                })
                    , "book"+index, "type"+index, random.nextDouble()*100);
        }
    }

    @Test
    public void collectorStream() {
        // 根据 ID 进行分组
        Map<Integer, List<Book>> bookGroups = Stream.generate(new BookSupplier())
                                                    .limit(100)
                                                    .collect(Collectors.groupingBy(Book::getId));
        // 遍历查看归组情况
        for (Map.Entry<Integer, List<Book>> bookEntry : bookGroups.entrySet()) {
            Map.Entry next = bookEntry;
            System.out.println("book " + next.getKey() + " = " + next.getValue());
        }
        // 按照价格是否小与 50 进行分区
        Map<Boolean, List<Book>> bookGroups2 = Stream.generate(new BookSupplier())
                                                        .limit(100)
                                                        .collect(Collectors.partitioningBy(p -> {return p.getPrice() < 50;}));
        System.out.println("bookGroups2.get(true) = " + bookGroups2.get(true));
        System.out.println("bookGroups2.get(false) = " + bookGroups2.get(false));
    }

    @Test
    public void testBuilderAndAtomic() {
//        User build = User.builder().id(1).build();
//        System.out.println("build = " + build.getId());

        AtomicReference<BigDecimal> testDe = new AtomicReference<>(new BigDecimal(0));
        BigDecimal finalTestDe = testDe.get();
        List<String> slist = new ArrayList<>();
        slist.add("1");
        slist.add("2");
        slist.add("3");
        // mean 1
        slist.forEach(e -> {
            testDe.set(finalTestDe.add(new BigDecimal(e)));
        });
        // BigDecimal 直接 add 是自身不累加的所以为0
        System.out.println("finalTestDe2 = " + finalTestDe);
        // 赋值给原子数据类型
        System.out.println("testDe = " + testDe);

        // mean2
        BigDecimal finalTestDe2 = new BigDecimal(0);
        for (String e : slist) {
            finalTestDe2 = finalTestDe2.add(new BigDecimal(e));
        }
        System.out.println("testDe = " + finalTestDe2);
    }
    @Test
    public void testMain() {

    }
}
